package com.polaris.live.common.util

import java.util.LinkedList
import kotlin.math.min
import kotlin.math.pow
import kotlin.reflect.KProperty

/**
 * LazyUtils
 *
 * @author Created by lucas on 2023/10/26 15:33
 */
object LazyUtils {

    /**
     * 计算指数退避的延迟时间
     *
     * @param attempt 尝试次数
     * @param initDelay 初始延迟
     * @param baseDelay 基础延迟
     * @param maxDelay 最大延迟
     * @param exponent 指数
     */
    fun exponentialBackoff(
        attempt: Int,
        initDelay: Long = 1000,
        baseDelay: Long = 1000,
        maxDelay: Long = 15 * 1000,
        exponent: Double? = null
    ): Long {
        return min(
            maxDelay, if (exponent != null) {
                initDelay + (baseDelay * exponent.pow(attempt.toDouble())).toLong()
            } else {
                initDelay + (baseDelay * attempt)
            }
        )
    }
}

class ResettableLazyManager {

    // we synchronize to make sure the timing of a reset() call and new inits do not collide
    private val managedDelegates = LinkedList<Resettable>()

    fun register(managed: Resettable) {
        synchronized(managedDelegates) {
            managedDelegates.add(managed)
        }
    }

    fun reset() {
        synchronized(managedDelegates) {
            managedDelegates.forEach { it.reset() }
            managedDelegates.clear()
        }
    }
}

fun interface Resettable {

    fun reset()

}

class ResettableLazy<T>(
    val manager: ResettableLazyManager,
    val mode: LazyThreadSafetyMode,
    val init: () -> T
) : Resettable {

    @Volatile
    var lazyHolder = makeInitBlock()

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return lazyHolder.value
    }

    override fun reset() {
        lazyHolder = makeInitBlock()
    }

    private fun makeInitBlock(): Lazy<T> {
        return lazy(mode) {
            manager.register(this)
            init()
        }
    }
}

fun <T> resettableLazy(
    manager: ResettableLazyManager,
    mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED,
    init: () -> T
): ResettableLazy<T> {
    return ResettableLazy(manager, mode, init)
}

fun resettableManager(): ResettableLazyManager = ResettableLazyManager()